package com.huiquan.analysis.service;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import com.huiquan.analysis.constants.ExecuteRuleConstant;
import com.huiquan.analysis.dao.AnalysisRuleDao;
import com.huiquan.analysis.domain.AnalysisRule;
import com.huiquan.analysis.domain.VocabularyTemp;
import com.huiquan.analysis.tree.AhoCorasickDoubleArrayTrieHelper;
import com.huiquan.analysis.utils.ListUtil;
import com.huiquan.foundation.util.BusinessUtil;

/**
 * 类说明
 *
 * @author leo
 * @time 2017年3月17日 上午10:58:28
 */
@Service
public class ExecuteRuleService {
    @Autowired
    private AnalysisRuleDao analysisRuleDao;

    private final Logger log = LoggerFactory.getLogger(getClass());

    /**
     * 对os标注后的数据执行规则处理
     *
     * @param inputFilePath  输入文件路径
     * @param outputFilePath 输出文件路径
     * @param type           类型:1-主诉,2-现病史,3-医学影像
     * @throws IOException
     */
    public boolean executeRule(String inputFilePath, String outputFilePath, int type) throws IOException {
        Map<String, Object> param = new HashMap<>();
        param.put("type", type);
        List<AnalysisRule> rules = analysisRuleDao.retrieveList(param);
        if (ListUtil.isEmpty(rules)) {
            log.info("没有找到规则");
            // return false;
        } else {
            log.info("ruleSize:" + rules.size());
        }
        executeRuleWithData(rules, inputFilePath, outputFilePath);
        log.info("rule处理完成");
        return true;
    }

    /**
     * 对数据进行规则处理
     *
     * @param tree     字典树
     * @param map      规则Map
     * @param tempList 数据
     */
    private void execute(AhoCorasickDoubleArrayTrieHelper tree, Map<String, AnalysisRule> map,
                         ArrayList<VocabularyTemp> tempList) {
        String vocabulary = this.getVocabularyFromVocabularyTempList(tempList);
        // 通过tree的全匹配得到该组词对应的ruleKey列表
        List<String> ruleKeyList = tree.getAllMatchWords(vocabulary);
        // 如果ruleKey列表为空，则说明该组词没有对应的规则需要处理
        if (ListUtil.isEmpty(ruleKeyList)) {
            return;
        }
        for (String key : ruleKeyList) {
            AnalysisRule rule = map.get(key);
            int ruleType = rule.getRuleType();
            if (ruleType == 1) { // 拆分
                executeSplit(rule, tempList);
            } else if (ruleType == 2) { // 合并
                executeMerge(rule, tempList);
            } else if (ruleType == 3) { // 更新
                executeUpdate(rule, tempList);
            } else if (ruleType == 4) { // 多对多
                executeMerge(rule, tempList);
                executeSplit(rule, tempList);
            }
        }

    }

    /**
     * 合并一组词的词语部分并输出
     *
     * @param tempList
     * @return
     */
    private String getVocabularyFromVocabularyTempList(ArrayList<VocabularyTemp> tempList) {
        StringBuilder sb = new StringBuilder();
        for (VocabularyTemp temp : tempList) {
            sb.append(temp.getVocabulary());
        }
        return sb.toString();
    }

    /**
     * 根据规则对数据进行拆分处理
     *
     * @param rule     规则
     * @param tempList 数据
     */
    private void executeSplit(AnalysisRule rule, ArrayList<VocabularyTemp> tempList) {
        String ruleVocabulary = rule.getVocabulary();
        String ruleStr = rule.getRule();
        ArrayList<VocabularyTemp> ruleTempList = splitRule(ruleStr);
        if (ruleTempList == null) {
            return;
        }
        VocabularyTemp temp;
        int index = 0;
        while (index < tempList.size()) {
            temp = tempList.get(index);
            // 如果词语与规则词语相等，则做拆分处理
            if (ruleVocabulary.equals(temp.getVocabulary())) {
                // 记录当前temp在列表中的序列
                int tempIndex = tempList.indexOf(temp);
                // 移除当前temp
                tempList.remove(index);
                // 倒序向temp的序列位置插入ruleTempList中的数据
                for (int i = ruleTempList.size() - 1; i >= 0; i--) {
                    tempList.add(tempIndex, ruleTempList.get(i));
                }
                index = index + ruleTempList.size() - 1;
            }
            index++;
        }
    }

    /**
     * 根据规则对数据进行合并处理
     *
     * @param rule     规则
     * @param tempList 数据
     */
    private void executeMerge(AnalysisRule rule, ArrayList<VocabularyTemp> tempList) {
        String ruleVocabulary = rule.getVocabulary();
        String ruleStr = rule.getRule();
        ArrayList<VocabularyTemp> ruleTempList = splitRule(ruleStr);
        VocabularyTemp ruleTemp;
        if (ListUtil.isEmpty(ruleTempList)) {
            log.info("merge error");
            return;
        }
        if (ruleTempList.size() == 1) { // size=1的时候，规则只拆分为一，认为是单独merge
            ruleTemp = ruleTempList.get(0);
        } else { // 因为size>1，所以认为这是多对多规则中的merge部分
            ruleTemp = new VocabularyTemp();
            ruleTemp.setVocabulary(ruleVocabulary);
        }
        VocabularyTemp temp;
        StringBuilder sb;
        int index = 0;
        while (index < tempList.size()) {
            temp = tempList.get(index);
            // 如果当前temp的词语是规则词语的开头部分
            if (ruleVocabulary.startsWith(temp.getVocabulary())) {
                // 记录当前temp在list中的序列位置
                int tempIndex = tempList.indexOf(temp);
                sb = new StringBuilder();
                int n = tempIndex;
                while (n < tempList.size()) {
                    sb.append(tempList.get(n).getVocabulary());
                    // 如果字符串长度小于ruleVocabulary的长度
                    if (sb.toString().length() < ruleVocabulary.length()) {
                        if (ruleVocabulary.startsWith(sb.toString())) {
                            // 如果字符串是规则词语的开头部分
                            n++;
                            continue;
                        } else {
                            break;
                        }
                    }
                    // 如果字符串长度等于ruleVocabulary的长度
                    if (sb.toString().length() == ruleVocabulary.length()) {
                        if (ruleVocabulary.equals(sb.toString())) {
                            // 如果字符串与规则词语相等，则进行合并处理
                            // 先移除
                            for (int i = 0; i < n - tempIndex + 1; i++) {
                                tempList.remove(tempIndex);
                            }
                            // 再添加
                            tempList.add(tempIndex, ruleTemp);
                            break;
                        } else {
                            break;
                        }
                    }
                    // 如果字符串长度大于ruleVocabulary的长度
                    if (sb.toString().length() > ruleVocabulary.length()) {
                        break;
                    }

                }
            }
            index++;
        }
    }

    /**
     * 根据规则对数据进行更新处理
     *
     * @param rule     规则
     * @param tempList 数据
     */
    private void executeUpdate(AnalysisRule rule, ArrayList<VocabularyTemp> tempList) {
        String ruleVocabulary = rule.getVocabulary();
        String ruleStr = rule.getRule();
        ArrayList<VocabularyTemp> ruleTempList = splitRule(ruleStr);
        if (ListUtil.isEmpty(ruleTempList)) {
            log.info("update error");
            return;
        }
        VocabularyTemp ruleTemp = ruleTempList.get(0);
        for (VocabularyTemp temp : tempList) {
            // 如果词语与规则词语相等，做更新处理
            if (ruleVocabulary.equals(temp.getVocabulary())) {
                temp.setVocabulary(ruleTemp.getVocabulary());
                temp.setCharacteristic(ruleTemp.getCharacteristic());
                temp.setProperty(ruleTemp.getProperty());
            }
        }
    }

    /**
     * 拆分规则语句得到VocabularyTemp列表
     *
     * @param ruleStr
     * @return
     */
    private ArrayList<VocabularyTemp> splitRule(String ruleStr) {
        if (StringUtils.isEmpty(ruleStr)) {
            return null;
        }
        ArrayList<VocabularyTemp> result = new ArrayList<>();
        String[] tempStrArray = ruleStr.split(";");
        VocabularyTemp vocabularyTemp;
        for (String tempStr : tempStrArray) {
            int firstUnderline = tempStr.indexOf("_", 0);
            int secondUnderline = tempStr.indexOf("_", firstUnderline + 1);
            vocabularyTemp = new VocabularyTemp();
            vocabularyTemp.setVocabulary(tempStr.substring(0, firstUnderline));
            vocabularyTemp.setCharacteristic(tempStr.substring(firstUnderline + 1, secondUnderline));
            vocabularyTemp.setProperty(tempStr.substring(secondUnderline + 1, tempStr.length()));
            result.add(vocabularyTemp);
        }
        return result;
    }

    /**
     * 输入文件脚本
     */
    @Value("${execute.rule.input}")
    private String inputScript;
    /**
     * 输出文件脚本
     */
    @Value("${execute.rule.output}")
    private String outputScript;

    /**
     * 读取输入文件，得到数据集合,进行规则处理
     *
     * @param rules         规则列表
     * @param inputFilePath 输入文件路径
     * @return
     * @throws IOException
     */
    private void executeRuleWithData(List<AnalysisRule> rules, String inputFilePath, String outputFilePath)
            throws IOException {
        if (ListUtil.isEmpty(rules)) {
            // 执行输入脚本，从181上读取输入文件
            String cmds[] = new String[2];
            cmds[0] = inputScript;
            cmds[1] = inputFilePath;
            BusinessUtil.excuteShell(cmds);
            try {
                File file = new File(ExecuteRuleConstant.LOCAL_FILE_INPUT_PATH);
                if (file.isFile() && file.exists()) { // 判断文件是否存在
                    InputStreamReader read = new InputStreamReader(new FileInputStream(file), "UTF-8");// 考虑到编码格式
                    BufferedReader bufferedReader = new BufferedReader(read);
                    String lineTxt;
                    OutputStreamWriter writer = new OutputStreamWriter(
                            new FileOutputStream(ExecuteRuleConstant.LOCAL_FILE_OUTPUT_PATH), "UTF-8");
                    while ((lineTxt = bufferedReader.readLine()) != null) {
                        writer.write(lineTxt);
                        writer.write("\n");
                    }
                    read.close();
                    writer.flush();
                    writer.close();
                    // 执行输出脚本，把文件上传到181上
                    cmds[0] = outputScript;
                    cmds[1] = outputFilePath;
                    BusinessUtil.excuteShell(cmds);
                } else {
                    log.info("找不到指定的文件");
                }
            } catch (IOException e) {
                log.info("读取文件内容出错");
                e.printStackTrace();
            }
        } else {
            // 生成rules对应的map，key是rule的vocabulary
            Map<String, AnalysisRule> map = new HashMap<>();
            for (AnalysisRule rule : rules) {
                map.put(rule.getVocabulary(), rule);
            }
            // 生成字典树
            Set<String> keySet = map.keySet();
            String[] keyArray = new String[keySet.size()];
            keyArray = keySet.toArray(keyArray);
            AhoCorasickDoubleArrayTrieHelper tree = new AhoCorasickDoubleArrayTrieHelper(keyArray); // 字典树
            // 执行输入脚本，从181上读取输入文件
            String cmds[] = new String[2];
            cmds[0] = inputScript;
            cmds[1] = inputFilePath;
            BusinessUtil.excuteShell(cmds);
            try {
                File file = new File(ExecuteRuleConstant.LOCAL_FILE_INPUT_PATH);
                if (file.isFile() && file.exists()) { // 判断文件是否存在
                    InputStreamReader read = new InputStreamReader(new FileInputStream(file), "UTF-8");// 考虑到编码格式
                    BufferedReader bufferedReader = new BufferedReader(read);
                    String lineTxt;
                    ArrayList<VocabularyTemp> tempList = new ArrayList<>();
                    VocabularyTemp temp;
                    OutputStreamWriter writer = new OutputStreamWriter(
                            new FileOutputStream(ExecuteRuleConstant.LOCAL_FILE_OUTPUT_PATH), "UTF-8");
                    while ((lineTxt = bufferedReader.readLine()) != null) {
                        if ("".equals(lineTxt)) {
                            execute(tree, map, tempList);
                            for (VocabularyTemp vocabularyTemp : tempList) {
                                writer.write(vocabularyTemp.getVocabulary());
                                writer.write("\t");
                                writer.write(vocabularyTemp.getCharacteristic());
                                writer.write("\t");
                                writer.write(vocabularyTemp.getProperty());
                                writer.write("\n");
                            }
                            writer.write("\n");
                            tempList.clear();
                        } else {
                            if (lineTxt.startsWith("#")) {
                                continue;
                            }
                            String[] s = lineTxt.split("\t");
                            temp = new VocabularyTemp();
                            temp.setVocabulary(s[0]);
                            temp.setCharacteristic(s[1]);
                            temp.setProperty(s[2]);
                            tempList.add(temp);
                        }
                    }
                    read.close();
                    writer.flush();
                    writer.close();
                    // 执行输出脚本，把文件上传到181上
                    cmds[0] = outputScript;
                    cmds[1] = outputFilePath;
                    BusinessUtil.excuteShell(cmds);
                } else {
                    log.info("找不到指定的文件");
                }
            } catch (IOException e) {
                log.info("读取文件内容出错");
                e.printStackTrace();
            }
        }
    }
}
